package service

import (
	"encoding/json"
	"errors"
	"github.com/gin-gonic/gin"
	"gluenet/cmd/glue-srvm/app"
	config "gluenet/configs"
	"gluenet/internal/glue-srvm/pkg/gluehttp"
	"gluenet/internal/glue-srvm/pkg/gluelog"
	"gluenet/pkg/agent"
	corev1 "gluenet/pkg/agent/types"
	"gluenet/pkg/agent/v2/rpc"
	apis2 "gluenet/pkg/apis"
	"gluenet/utils/rpc/v2/define"
	"io"
	"net/http"
)

const (
	QueryLevel0 = "0"
	QueryLevel1 = "1"
	QueryLevel2 = "2"
	QueryLevel3 = "3"
)

// Query 查询指标-prom-点
func Query(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		agentGuid := c.Param("agent_guid")
		//guid := c.Param("guid")
		level := c.Param("level")
		query := c.Query("query")
		time := c.Query("time")
		switch level {
		case QueryLevel0:
			resp := map[string]interface{}{}
			promUrl := config.GetConfig().PromAddr + "?query=" + query + "&time=" + time
			gluelog.Info("query -> ", promUrl)
			res, err := http.Get(promUrl)
			if err != nil {
				gluelog.Errorf("failed to request prometheus", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			bts, err := io.ReadAll(res.Body)
			if err != nil {
				gluelog.Errorf("failed to read response body", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			_ = json.Unmarshal(bts, &resp)
			gluehttp.Response(c, resp)
		case QueryLevel1:
		case QueryLevel2:
		case QueryLevel3:
			resp := map[string]interface{}{}
			cli, err := agent.NewAgent(&rpc.Config{
				Target:  agentGuid,
				NatsUrl: config.GetConfig().NatsServerAddr,
			})
			if err != nil {
				gluelog.Errorf("rpc url is empty", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			rlt, err := cli.Prom().PromQuery(&corev1.PromQuery{
				Query: query,
				Time:  time,
			})
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			_ = json.Unmarshal(rlt, &resp)
			gluehttp.Response(c, resp["data"])
		default:
			err := errors.New("invalid level")
			gluelog.Errorf("invalid level", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
	}
}

// QueryRange 查询指标-prom-区间
func QueryRange(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		agentGuid := c.Param("agent_guid")
		//guid := c.Param("guid")
		level := c.Param("level")
		query := c.Query("query")
		start := c.Query("start")
		end := c.Query("end")
		step := c.Query("step")
		switch level {
		case QueryLevel0:
			resp := map[string]interface{}{}
			promUrl := config.GetConfig().PromAddr + "?query=" + query + "&start=" + start + "&end=" + end + "&step=" + step
			gluelog.Info("query -> ", promUrl)
			res, err := http.Get(promUrl)
			if err != nil {
				gluelog.Errorf("failed to request prometheus", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			bts, err := io.ReadAll(res.Body)
			if err != nil {
				gluelog.Errorf("failed to read response body", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			_ = json.Unmarshal(bts, &resp)
			gluehttp.Response(c, resp)
		case QueryLevel1:
		case QueryLevel2:
		case QueryLevel3:
			resp := map[string]interface{}{}
			cli, err := agent.NewAgent(&rpc.Config{
				Target:  agentGuid,
				NatsUrl: config.GetConfig().NatsServerAddr,
			})
			if err != nil {
				gluelog.Errorf("rpc url is empty", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			rlt, err := cli.Prom().PromQueryRange(&corev1.PromQueryRange{
				Query: query,
				Start: start,
				End:   end,
				Step:  step,
			})
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			_ = json.Unmarshal(rlt, &resp)
			gluehttp.Response(c, resp["data"])
		default:
			err := errors.New("invalid level")
			gluelog.Errorf("invalid level", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
	}
}

// LogCounts 查询指标-log-统计
func LogCounts(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		agentGuid := c.Param("agent_guid")
		guid := c.Param("guid")
		level := c.Param("level")
		start := c.Query("start")
		container := c.Query("container")
		end := c.Query("end")
		bucket := c.Query("bucket")
		switch level {
		case QueryLevel0:
		case QueryLevel1:
		case QueryLevel2:
		case QueryLevel3:
			cli, err := agent.NewAgent(&rpc.Config{
				Target:  agentGuid,
				NatsUrl: config.GetConfig().NatsServerAddr,
			})
			if err != nil {
				gluelog.Errorf("rpc url is empty", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			rlt, err := cli.Log().LogCounts(&corev1.LogCounts{
				Start:     start,
				Guid:      guid,
				Container: container,
				End:       end,
				Bucket:    bucket,
			})
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			gluehttp.Response(c, rlt)
		default:
			err := errors.New("invalid level")
			gluelog.Errorf("invalid level", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
	}
}

// LogInfo 查询指标-log-日志信息
func LogInfo(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		agentGuid := c.Param("agent_guid")
		guid := c.Param("guid")
		level := c.Param("level")
		start := c.Query("start")
		container := c.Query("container")
		end := c.Query("end")
		switch level {
		case QueryLevel0:
		case QueryLevel1:
		case QueryLevel2:
		case QueryLevel3:
			cli, err := agent.NewAgent(&rpc.Config{
				Target:  agentGuid,
				NatsUrl: config.GetConfig().NatsServerAddr,
			})
			if err != nil {
				gluelog.Errorf("rpc url is empty", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			rlt, err := cli.Log().LogInfo(&corev1.LogInfo{
				Start:     start,
				End:       end,
				Guid:      guid,
				Container: container,
			})
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			gluehttp.Response(c, rlt)
		default:
			err := errors.New("invalid level")
			gluelog.Errorf("invalid level", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
	}
}

// JaegerService 查询指标-jaeger-service
func JaegerService(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		agentGuid := c.Param("agent_guid")
		guid := c.Param("guid")
		level := c.Param("level")
		switch level {
		case QueryLevel0:
		case QueryLevel1:
		case QueryLevel2:
		case QueryLevel3:
			var resp map[string]interface{}
			cli, err := agent.NewAgent(&rpc.Config{
				Target:  agentGuid,
				NatsUrl: config.GetConfig().NatsServerAddr,
			})
			if err != nil {
				gluelog.Errorf("rpc url is empty", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			rlt, err := cli.Jaeger().JaegerService(&corev1.JaegerService{
				URL:  c.FullPath(),
				Guid: guid,
			})
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			_ = json.Unmarshal(rlt, &resp)
			gluehttp.Response(c, resp)
		default:
			err := errors.New("invalid level")
			gluelog.Errorf("invalid level", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
	}
}

// JaegerOperate 查询指标-jaeger-operate
func JaegerOperate(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		agentGuid := c.Param("agent_guid")
		guid := c.Param("guid")
		level := c.Param("level")
		service := c.Query("service")
		switch level {
		case QueryLevel0:
		case QueryLevel1:
		case QueryLevel2:
		case QueryLevel3:
			var resp map[string]interface{}
			cli, err := agent.NewAgent(&rpc.Config{
				Target:  agentGuid,
				NatsUrl: config.GetConfig().NatsServerAddr,
			})
			if err != nil {
				gluelog.Errorf("rpc url is empty", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			rlt, err := cli.Jaeger().JaegerOperate(&corev1.JaegerOperate{
				URL:     c.FullPath(),
				Guid:    guid,
				Service: service,
			})
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			_ = json.Unmarshal(rlt, &resp)
			gluehttp.Response(c, resp)
		default:
			err := errors.New("invalid level")
			gluelog.Errorf("invalid level", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
	}
}

// JaegerTraces 查询指标-jaeger-traces
func JaegerTraces(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		agentGuid := c.Param("agent_guid")
		guid := c.Param("guid")
		level := c.Param("level")
		service := c.Query("service")
		operation := c.Query("operation")
		limit := c.Query("limit")
		start := c.Query("start")
		end := c.Query("end")
		lookBack := c.Query("look_back")
		maxDuration := c.Query("max_duration")
		minDuration := c.Query("min_duration")
		tags := c.Query("tags")
		switch level {
		case QueryLevel0:
		case QueryLevel1:
		case QueryLevel2:
		case QueryLevel3:
			var resp map[string]interface{}
			cli, err := agent.NewAgent(&rpc.Config{
				Target:  agentGuid,
				NatsUrl: config.GetConfig().NatsServerAddr,
			})
			if err != nil {
				gluelog.Errorf("rpc url is empty", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			rlt, err := cli.Jaeger().JaegerTraces(&corev1.JaegerTraces{
				URL:         c.FullPath(),
				Guid:        guid,
				Service:     service,
				Operation:   operation,
				Limit:       limit,
				Start:       start,
				End:         end,
				LookBack:    lookBack,
				MaxDuration: maxDuration,
				MinDuration: minDuration,
				Tags:        tags,
			})
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			_ = json.Unmarshal(rlt, &resp)
			gluehttp.Response(c, resp)
		default:
			err := errors.New("invalid level")
			gluelog.Errorf("invalid level", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
	}
}

// CprofileFlame 查询指标-cprofile
func CprofileFlame(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		agentGuid := c.Param("agent_guid")
		guid := c.Param("guid")
		level := c.Param("level")
		pod := c.Query("pod")
		container := c.Query("container")
		switch level {
		case QueryLevel0:
		case QueryLevel1:
		case QueryLevel2:
		case QueryLevel3:
			//resp := map[string]interface{}{}
			cli, err := agent.NewAgent(&rpc.Config{
				Target:  agentGuid,
				NatsUrl: config.GetConfig().NatsServerAddr,
			})
			if err != nil {
				gluelog.Errorf("rpc url is empty", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			rlt, err := cli.Cprofile().CprofileFlame(&corev1.CprofileFlame{
				Pod:       pod,
				Container: container,
				Guid:      guid,
			})
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			//_ = json.Unmarshal(rlt, &resp)
			gluehttp.Response(c, rlt)
		default:
			err := errors.New("invalid level")
			gluelog.Errorf("invalid level", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
	}
}

// CprofileFile 查询指标-cprofile-file
func CprofileFile(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		agentGuid := c.Param("agent_guid")
		//guid := c.Param("guid")
		level := c.Param("level")
		url := c.Query("url")
		pod := c.Query("pod")
		container := c.Query("container")
		switch level {
		case QueryLevel0:
		case QueryLevel1:
		case QueryLevel2:
		case QueryLevel3:
			//resp := map[string]interface{}{}
			cli, err := agent.NewAgent(&rpc.Config{
				Target:  agentGuid,
				NatsUrl: config.GetConfig().NatsServerAddr,
			})
			if err != nil {
				gluelog.Errorf("rpc url is empty", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			rlt, err := cli.Cprofile().CprofileFile(&corev1.CprofileFile{
				URL:       url,
				Pod:       pod,
				Container: container,
			})
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			//_ = json.Unmarshal(rlt, &resp)
			gluehttp.Response(c, rlt)
		default:
			err := errors.New("invalid level")
			gluelog.Errorf("invalid level", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
	}
}

// DagServiceMap 查询指标-service-map
func DagServiceMap(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		agentGuid := c.Param("agent_guid")
		guid := c.Param("guid")
		level := c.Param("level")
		end := c.Query("end")
		switch level {
		case QueryLevel0:
		case QueryLevel1:
		case QueryLevel2:
		case QueryLevel3:
			cli, err := agent.NewAgent(&rpc.Config{
				Target:  agentGuid,
				NatsUrl: config.GetConfig().NatsServerAddr,
			})
			if err != nil {
				gluelog.Errorf("rpc url is empty", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			rlt, err := cli.Dag().DagServiceMap(&define.DagServiceMap{
				Guid: guid + "#" + end,
			})
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.ErrorResponse(c, *gluehttp.NewError(http.StatusInternalServerError, err.Error(), err))
				return
			}
			gluehttp.Response(c, rlt)
		default:
			err := errors.New("invalid level")
			gluelog.Errorf("invalid level", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
	}
}

// DagResponse 查询指标-cilium-response
func DagResponse(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		agentGuid := c.Param("agent_guid")
		level := c.Param("level")
		sourcePod := c.Query("source_pod")
		destinationPod := c.Query("destination_pod")
		port := c.Query("port")
		verdict := c.Query("verdict")
		url := c.Query("url")
		method := c.Query("method")
		protocol := c.Query("protocol")
		guid := c.Param("guid")
		switch level {
		case QueryLevel0:
		case QueryLevel1:
		case QueryLevel2:
		case QueryLevel3:
			cli, err := agent.NewAgent(&rpc.Config{
				Target:  agentGuid,
				NatsUrl: config.GetConfig().NatsServerAddr,
			})
			if err != nil {
				gluelog.Errorf("rpc url is empty", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			rlt, err := cli.Dag().DagResponse(&define.DagResponse{
				SourcePod:      sourcePod,
				DestinationPod: destinationPod,
				Port:           port,
				Verdict:        verdict,
				Url:            url,
				Method:         method,
				Protocol:       protocol,
				Guid:           guid,
			})
			if err != nil {
				gluelog.Errorf("failed to request rpc", err.Error())
				gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
				return
			}
			gluehttp.Response(c, rlt)
		default:
			err := errors.New("invalid level")
			gluelog.Errorf("invalid level", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
	}
}

func QueryRouter(router *gin.RouterGroup) func(app *app.Application) {
	return func(a *app.Application) {
		v1 := router.Group("/apis/monitor/:agent_guid")
		{
			promApi := v1.Group("/prom")
			{
				promApi.GET("/:guid/:level/query", Query(a))
				promApi.GET("/:guid/:level/query_range", QueryRange(a))
			}
			logApi := v1.Group("/log")
			{
				logApi.GET("/:guid/:level/counts", LogCounts(a))
				logApi.GET("/:guid/:level/info", LogInfo(a))
			}
			jaegerApi := v1.Group("/jaeger")
			{
				jaegerApi.GET("/:guid/:level/api/services", JaegerService(a))
				jaegerApi.GET("/:guid/:level/api/services/:service/operations", JaegerOperate(a))
				jaegerApi.GET("/:guid/:level/api/traces", JaegerTraces(a))
			}
			cprofileApi := v1.Group("/cprofile")
			{
				cprofileApi.GET("/:guid/:level/flame", CprofileFlame(a))
				cprofileApi.GET("/:guid/:level/file", CprofileFile(a))
			}
			dagApi := v1.Group("/dag")
			{
				dagApi.GET("/:guid/:level/service_map", DagServiceMap(a))
				dagApi.GET("/:guid/:level/responses", DagResponse(a))
			}
		}
	}
}

func init() {
	app.Router(QueryRouter)
}
